package stringFunctions;

import function.unary.UnaryFunction;
import function.unary.UnaryPredicate;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import myAnnotations.Constant;

/**
 *
 * Fully Tested: No
 * Fully Documented: Yes
 * Created: 8-Apr-2012
 * Last Updated: 8-Apr-2012
 *
 * @author Shimu Wu
 */
public class StringFunctions {

    /**
     * StringFunctions class has no instance methods. Only public static
     * methods. Private constructor to prevent creating StringFunctions
     * instances.
     */
    private StringFunctions() {
    }

    // ======================= BOOLEAN METHODS =======================
    /**
     * Returns true if the given str has all upper case letters. False
     * otherwise.
     *
     * @param str
     * @return true if the given str has all upper case letters. False
     * otherwise.
     */
    @Constant
    public static boolean isUpper(String str) {
        String upper = str.toUpperCase();
        return (upper.equals(str));
    }

    /**
     * Returns true if the given str has all lower case letters. False
     * otherwise.
     *
     * @param str
     * @return true if the given str has all lower case letters. False
     * otherwise.
     */
    @Constant
    public static boolean isLower(String str) {
        String lower = str.toLowerCase();
        return (lower.equals(str));
    }

    // ======================= UnaryFunction Methods =======================
    /**
     * Returns a UnaryFunction that returns a Matcher for a given String.
     * 
     * @param regex
     * @return 
     */
    public static UnaryFunction<String, Matcher> getRegexMatcher(final String regex) {
        return new UnaryFunction<String, Matcher>() {

            private final Pattern PATTERN = Pattern.compile(regex);

            @Override
            public Matcher evaluate(String arg) {
                return PATTERN.matcher(arg);
            }
        };
    }

    /**
     * Returns a UnaryFunction that returns true if and only if a given String 
     * satisfies the given regular expression.
     * @param regex
     * @return 
     */
    public static UnaryPredicate<String> getRegexPredicate(final String regex) {
        return new UnaryPredicate<String>() {

            private final Pattern PATTERN = Pattern.compile(regex);

            public Boolean evaluate(String arg) {
                return PATTERN.matcher(arg).matches();
            }
        };
    }

    // ======================= Returns a new String =======================
    /**
     * Appends all the args together, separated by the given separator.
     * There is an extra separator at the end of the String.
     * 
     * @param size the exact (or estimated) length of the final String
     * @param separator 
     * @param args
     * @return 
     */
    @Constant
    public static String concat(String separator, int size,
            Object... args) {

        StringBuilder b = new StringBuilder(size);
        for (Object arg : args) {
            if ((arg != null) && (!arg.toString().isEmpty())) {
                b.append(arg.toString());
                b.append(separator);
            }
        }
        return b.toString();
    }

    /**
     * 
     * @param size
     * @param args
     * @return 
     */
    @Constant
    public static String concat(int size, Object... args) {
        return concat("", size, args);
    }

    /**
     * 
     * @param args
     * @return 
     */
    @Constant
    public static String concat(Object... args) {
        return concat("", 100, args);
    }

    /**
     * Returns a String with that is the same as the given string's substring
     * from 0 (inclusive) to length (exclusive) plus ...
     * @param str
     * @return 
     */
    public static String truncate(String str, int length) {
        if (str.length() <= length) {
            return str;
        }
        return str.substring(0, length) + "...";
    }

    /**
     * Replaces each CharSequence in the given oldSequence with each 
     * CharSequence in the given newSequence. 
     * 
     * @param str
     * @param oldSequence
     * @param newSequence
     * @throws IllegalArgumentException if the length of oldSequence and newSequence
     * is not equal
     * @return 
     */
    public static String replace(String str, CharSequence[] oldSequence,
            CharSequence[] newSequence) {
        if (oldSequence.length != newSequence.length) {
            throw new IllegalArgumentException();
        }
        String temp = str;
        for (int i = 0; i < oldSequence.length; i++) {
            temp = temp.replace(oldSequence[i], newSequence[i]);
        }
        return temp;
    }

    /**
     * Escapes special characters (metacharacters) used by Java's regular
     * expressions, which includes: the opening square bracket [, 
     * the backslash \, the caret ^, the dollar sign $, the period or dot ., 
     * the vertical bar or pipe symbol |, the question mark ?, the asterisk or 
     * star *, the plus sign +, the opening round bracket ( and the closing 
     * round bracket ).
     * 
     * @param str
     * @return 
     */
    public static String escape(String str) {
        return escape(str, new char[]{'[', '\\', '^', '$',
                    '.', '|', '?', '*', '+', '(', ')'});
    }

    /**
     * Returns a new String that escapes (prepends a backslash symbol '\' to) 
     * all of the chars in escapeChars that are present in the given String. 
     * @param str
     * @param escapeChars
     * @return 
     */
    public static String escape(String str, char[] escapeChars) {
        final int length = escapeChars.length;
        String[] oldChars = new String[length];
        String[] newChars = new String[length];
        for (int i = 0; i < length; i++) {
            oldChars[i] = String.valueOf(escapeChars[i]);
            newChars[i] = "\\" + escapeChars[i];
        }
        return replace(str, oldChars, newChars);
    }

    public static String insertSeparator(Iterable<String> characters, 
            String separator) {
        Iterator<String> iter = characters.iterator();
        if (!iter.hasNext()) {
            return "";
        }
        StringBuilder b = new StringBuilder(10 * (10 + separator.length()));
        b.append(iter.next());
        while(iter.hasNext()) {
            b.append(separator);
            b.append(iter.next());
        }
        return b.toString();
    }
    
    public static String insertSeparator(String[] characters, 
            String separator) {
        if (characters.length == 0) {
            return "";
        }
        StringBuilder b = new StringBuilder(10 * (10 + separator.length()));
        b.append(characters[0]);
        for(int i = 1; i < characters.length; i++) {
            b.append(separator);
            b.append(characters[i]);
        }
        return b.toString();
    }
    
    public static void appendAll(StringBuilder b, Object... strs) {
        for (Object obj : strs) {
            b.append(obj);
        }
    }
}
